package cn.com.doone.common.uc.oauth.authorize;

import static cn.com.doone.common.uc.domain.oauth.Constants.OAUTH_LOGIN_VIEW;
import static cn.com.doone.common.uc.domain.oauth.Constants.REQUEST_USERNAME;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.com.doone.common.uc.domain.oauth.AccessToken;
import cn.com.doone.common.uc.oauth.OAuthAuthxRequest;
import cn.com.doone.common.uc.oauth.shiro.OAuth2Token;
import cn.com.doone.common.uc.oauth.validator.AbstractClientDetailsValidator;
import cn.com.doone.common.uc.oauth.validator.WedoClientDetailValidator;
import cn.com.doone.common.uc.utils.DES3;
import cn.com.doone.common.uc.utils.HttpClientUtil;
import cn.com.doone.common.uc.utils.MD5;
import cn.com.doone.common.uc.utils.PropertiesUtils;
import cn.com.doone.common.uc.web.WebUtils;

import com.alibaba.fastjson.JSON;

public class WedoAuthorizeHandler extends AbstractAuthorizeHandler {

	private static final Logger LOG = LoggerFactory.getLogger(WedoAuthorizeHandler.class);

	private AccessToken accessToken;

	private PropertiesUtils propertiesUtils;

	public WedoAuthorizeHandler(OAuthAuthxRequest oauthRequest, HttpServletResponse response,
			PropertiesUtils propertiesUtils) {
		super(oauthRequest, response);
		this.propertiesUtils = propertiesUtils;
	}

	@Override
	protected AbstractClientDetailsValidator getValidator() {
		return new WedoClientDetailValidator(oauthRequest, oauthRequest.request());
	}

	public void handle() throws Exception {

		LOG.debug("######################   登录跳转START   ###########################");

		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

		LOG.debug("WEDO身份认证开始时间：" + df.format(new Date()));

		if (wedoValid()) {
			return;
		}

		LOG.debug("WEDO身份认证结束时间：" + df.format(new Date()));

		// valiblackip
		/*
		 * if(valiBlackIP()) { return; }
		 */

		LOG.debug("请求合法性认证开始时间：" + df.format(new Date()));

		// validate
		if (validateFailed()) {
			return;
		}

		LOG.debug("请求合法性认证结束时间：" + df.format(new Date()));

		LOG.debug("是否需要登录认证开始时间：" + df.format(new Date()));
		// Check need usr login
		if (goLogin()) {
			return;
		}

		LOG.debug("是否需要登录认证开始时间：" + df.format(new Date()));

		// 认证IP锁
		/*
		 * if (validIpLock()){ return; }
		 */

		// 是否需要登陆
		/*
		 * if (isSubmitLogin()) { // 账号锁 if (validAccountLock()) { return; } }
		 */

		LOG.debug("登录请求认证开始时间：" + df.format(new Date()));

		// submit login
		if (submitLogin()) {
			return;
		}

		LOG.debug("登录请求认证结束时间：" + df.format(new Date()));
		// 验证是否为弱密码
		/*
		 * if (isWeakPassword()) { return; }
		 */

		LOG.debug("获取账号及应用信息开始时间：" + df.format(new Date()));

		// 获取并设置userId和appInfoId
		this.setAttribute();

		LOG.debug("获取账号及应用信息结束时间：" + df.format(new Date()));

		LOG.debug("用户组权限认证开始时间：" + df.format(new Date()));
		// vali authorize
		if (valiAuthorize()) {
			return;
		}
		LOG.debug("用户组权限认证结束时间：" + df.format(new Date()));

		LOG.debug("用户自身授权认证开始时间：" + df.format(new Date()));
		// Check approval
		if (goApproval()) {
			return;
		}

		// Submit approval
		if (submitApproval()) {
			return;
		}
		LOG.debug("用户自身授权认证结束时间：" + df.format(new Date()));

		// handle response
		handleResponse();
	}

	@SuppressWarnings("unchecked")
	private boolean wedoValid() throws Exception {

		SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

		HttpServletRequest request = oauthRequest.request();

		String return_params = request.getParameter("returnParams");
		String token = request.getParameter("token");
		String staffId = request.getParameter("staffId");
		String appKey = request.getParameter("appKey");
		String loginTime = request.getParameter("loginTime");
		String version = request.getParameter("version");
		String secret = request.getParameter("secret");
		String intVersion = request.getParameter("intVersion");
		String callUrl = request.getParameter("callUrl");
		String authenticator = request.getParameter("authenticator");
		String ucAppSecret = request.getParameter("ucAppSecret");
		String clientId = request.getParameter("client_id");
		String clientSecret = request.getParameter("client_secret");

		LOG.debug("------------------>callUrl:" + callUrl);
		LOG.debug("------------------>appKey:" + appKey);
		LOG.debug("------------------>loginTime:" + loginTime);
		LOG.debug("------------------>intVersion:" + intVersion);
		LOG.debug("------------------>clientId:" + clientId);
		LOG.debug("------------------>clientSecret:" + clientSecret);

		if (intVersion != null && !intVersion.isEmpty() && !"null".equals(intVersion)) {

			String checkAhthenticator = MD5
					.getMD5Str(DES3.encode(HttpClientUtil.encodeURIComponent(callUrl + appKey + loginTime + intVersion),
							propertiesUtils.getSecretKey()));

			LOG.debug("------------------>authenticator:" + authenticator);
			LOG.debug("------------------>checkAhthenticator:" + checkAhthenticator);

			if (!authenticator.equals(checkAhthenticator.toLowerCase())) {
				final OAuthResponse oAuthResponse = OAuthASResponse
						.authorizationResponse(oauthRequest.request(), HttpServletResponse.SC_FOUND)
						// .location(clientDetails.getRedirectUri())
						.location(request.getContextPath() + "/resources/no_auth_error.html").buildQueryMessage();

				WebUtils.writeOAuthQueryResponse(response, oAuthResponse);

				return true;
			}

		}

		List<NameValuePair> params = new ArrayList<NameValuePair>();
		params.add(new BasicNameValuePair("return_params", return_params));
		params.add(new BasicNameValuePair("token", token));
		params.add(new BasicNameValuePair("staffId", staffId));
		params.add(new BasicNameValuePair("appKey", appKey));

		if (intVersion != null && !intVersion.isEmpty() && !"null".equals(intVersion)) {
			String authenticatorToken = MD5.getMD5Str(DES3.encode((token + appKey), propertiesUtils.getSecretKey()));
			params.add(new BasicNameValuePair("int_version", intVersion));
			params.add(new BasicNameValuePair("app_key", appKey));
			params.add(new BasicNameValuePair("authenticator", authenticatorToken.toLowerCase()));
		}

		LOG.debug("------------------>return_params:" + return_params);
		LOG.debug("------------------>token:" + token);
		LOG.debug("------------------>staffId:" + staffId);
		LOG.debug("------------------>appKey:" + appKey);
		LOG.debug("------------------>loginTime:" + loginTime);
		LOG.debug("------------------>version:" + version);
		LOG.debug("------------------>secret:" + secret);

		LOG.debug("从wedo换取用户信息开始时间：" + df.format(new Date()));
		Map<String, Object> userInfoMap = HttpClientUtil
				.doPost(propertiesUtils.getWedoServerPath() + "oa/APP_GET_USRINFO_BY_TOKEN.action", params);
		LOG.debug("从wedo换取用户信息结束时间：" + df.format(new Date()));

		System.out.println("------------------------>userInfoMap" + JSON.toJSONString(userInfoMap));

		/*
		 * Map<String, Object> userInfoMap = new HashMap();
		 * 
		 * userInfoMap.put("return_params", "用户信息！！"); userInfoMap.put("return_msg",
		 * "成功"); userInfoMap.put("return_code", "0");
		 */

		if ("0".equals(userInfoMap.get("return_code"))) {

			Map<String, Object> returnParams = (Map<String, Object>) userInfoMap.get("return_params");

			LOG.debug("用wedo账号获取OA账号开始时间：" + df.format(new Date()));

			Map<String, Object> ucUserMap = new HashMap<String, Object>();
			if ((String) returnParams.get("user_info_id") != null) {
				ucUserMap = oauthService.findUserInfoByUserId((String) returnParams.get("user_info_id"));
			} else {
				ucUserMap = oauthService.findUserInfoByAppAccount("WEDO", (String) returnParams.get("staff_id"));
			}

			LOG.debug("用wedo账号获取OA账号结束时间：" + df.format(new Date()));

			if (ucUserMap != null) {
				request.setAttribute(REQUEST_USERNAME, ucUserMap.get("USER_ACCOUNT"));
				request.setAttribute("USER_INFO_ID", ucUserMap.get("USER_INFO_ID"));

				if (intVersion != null && !intVersion.isEmpty() && !"null".equals(intVersion)) {
					request.setAttribute("client_secret", ucAppSecret);
					request.setAttribute("redirect_uri", callUrl);

					LOG.debug("2：client_secret：" + ucAppSecret);
					LOG.debug("2：redirect_uri：" + callUrl);

				} else {
					request.setAttribute("client_secret", "XDW_OA");

					Map<String, Object> reParams = JSON.parseObject(return_params, HashMap.class);

					request.setAttribute("redirect_uri", reParams.get("url"));

					LOG.debug("1：client_secret：" + "XDW_OA");
					LOG.debug("1：redirect_uri：" + reParams.get("url"));

				}

			} else {
				final OAuthResponse oAuthResponse = OAuthASResponse
						.authorizationResponse(oauthRequest.request(), HttpServletResponse.SC_FOUND)
						// .location(clientDetails.getRedirectUri())
						.location(request.getContextPath() + "/resources/oa_not_auth_error.html").buildQueryMessage();

				WebUtils.writeOAuthQueryResponse(response, oAuthResponse);
				return true;
			}
		} else {

			final OAuthResponse oAuthResponse = OAuthASResponse
					.authorizationResponse(oauthRequest.request(), HttpServletResponse.SC_FOUND)
					// .location(clientDetails.getRedirectUri())
					.location(request.getContextPath() + "/resources/wedo_error.html").buildQueryMessage();

			WebUtils.writeOAuthQueryResponse(response, oAuthResponse);

			return true;
		}

		/*
		 * request.setAttribute(REQUEST_USERNAME, "lizm");
		 * request.setAttribute("client_secret", "XDW_OA");
		 * request.setAttribute("redirect_uri",
		 * "http://www.done.com:8292/CodeGeneratorWeb/mobile/index.jsp");
		 */

		LOG.debug("根据OA账号获取ACCESS_TOKEN开始时间：" + df.format(new Date()));
		accessToken = oauthService.loadAccessToken(clientId, (String) request.getAttribute(REQUEST_USERNAME), null);
		LOG.debug("根据OA账号获取ACCESS_TOKEN结束时间：" + df.format(new Date()));
		// accessToken = oauthService.loadAccessToken("XDW_OA", "lizm", null);*/

		System.out.println("------------->accessToken" + JSON.toJSONString(accessToken));

		LOG.debug("ACCESS_TOKEN合法性认证开始时间：" + df.format(new Date()));
		if (accessToken != null) {
			if (accessToken.tokenExpired()) { // AccessToken已过期
				if (accessToken.refreshTokenExpired()) { // 刷新令牌已过期
					accessToken = oauthService.retrieveNewAccessTokenByUsername(clientDetails(),
							(String) request.getAttribute(REQUEST_USERNAME));
				} else { // 刷新令牌已过期
					accessToken = oauthService.changeAccessTokenByRefreshToken(accessToken.refreshToken(),
							accessToken.clientId());
				}
			}
		} else {
			accessToken = oauthService.retrieveNewAccessTokenByUsername(clientDetails(),
					(String) request.getAttribute(REQUEST_USERNAME));
		}
		LOG.debug("ACCESS_TOKEN合法性认证结束时间：" + df.format(new Date()));

		// accessToken = oauthService.retrieveNewAccessTokenByUsername(clientDetails(),
		// (String) request.getAttribute(REQUEST_USERNAME));
		return false;
	}

	@Override
	protected boolean submitLogin() throws ServletException, IOException {
		if (isSubmitLogin()) {
			// login flow
			try {
				final HttpServletRequest request = oauthRequest.request();

				System.out.println("------------------------->" + request.getAttribute(REQUEST_USERNAME));

				AuthenticationToken token = new OAuth2Token(accessToken.tokenId(), "os-resource")
						.setUserId((String) request.getAttribute(REQUEST_USERNAME));

				SecurityUtils.getSubject().login(token);

				// 保存登录日志
				this.logLogin((String) request.getAttribute(REQUEST_USERNAME), "1");

				// 更新账号锁定状态
				Map<String, Object> userMap = new HashMap<String, Object>();
				int userInfoId = this.oauthService
						.findUserInfoIdByAccount((String) request.getAttribute(REQUEST_USERNAME));
				userMap.put("userInfoId", userInfoId);
				userMap.put("lockLevel", "0");
				userMap.put("invalidTime", null);

				this.oauthService.updateUserLock(userMap);

				LOG.debug("Submit login successful");

				this.userFirstLogged = true;
				return false;
			} catch (Exception ex) {

				ex.printStackTrace();
				// login failed
				LOG.debug("Login failed, back to login page too", ex);
				final HttpServletRequest request = oauthRequest.request();

				// 保存登录日志
				this.logLogin((String) request.getAttribute(REQUEST_USERNAME), "0");

				request.setAttribute("username", request.getAttribute(REQUEST_USERNAME));
				request.setAttribute("oauth_login_error", true);
				request.setAttribute("oauth_login_error_message", "用户名或密码错误！！");
				request.getRequestDispatcher(OAUTH_LOGIN_VIEW).forward(request, response);
				return true;
			}
		}
		return false;
	}

	protected void responseApprovalDeny() throws IOException, OAuthSystemException {

		String redirectUri = (String) oauthRequest.request().getAttribute("redirect_uri");
		// redirectUri = redirectUri.replaceAll("%26", "&");

		final OAuthResponse oAuthResponse = OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND)
				.setError(OAuthError.CodeResponse.ACCESS_DENIED).setErrorDescription("User denied access")
				// .location(clientDetails().getRedirectUri())
				.location(redirectUri).setState(oauthRequest.getState()).buildQueryMessage();
		LOG.debug("'ACCESS_DENIED' response: {}");

		WebUtils.writeOAuthQueryResponse(response, oAuthResponse);

		// user logout when deny
		final Subject subject = SecurityUtils.getSubject();
		subject.logout();
		LOG.debug("After 'ACCESS_DENIED' call logout. user: {}");
	}

	@Override
	protected void handleResponse() throws OAuthSystemException, IOException {

		String redirectUri = (String) oauthRequest.request().getAttribute("redirect_uri");

		final OAuthResponse oAuthResponse = OAuthASResponse
				.authorizationResponse(oauthRequest.request(), HttpServletResponse.SC_FOUND).location(redirectUri)
				.setParam("loginCheckId", String.valueOf(oauthRequest.request().getAttribute("USER_INFO_ID")))
				.setParam("appCode", "WEDO").buildQueryMessage();

		// Map<String,Object> logMap = new HashMap<String,Object>();
		// logMap.put("userAccount", request.getParameter(REQUEST_USERNAME));
		// logMap.put("logType", "WEDO");
		// logMap.put("app", "XDW_UC_MANAGER");
		// LOG.info(JSON.toJSONString(logMap));

		WebUtils.writeOAuthQueryResponse(response, oAuthResponse);
	}

}
